package com.lianqi.emcpframework.core.boost.aop;

import com.lianqi.emcpframework.utils.AopUtils;
import com.lianqi.emcpframework.utils.AssembleTools;
import com.lianqi.emcpframework.utils.ThreadLocalUtils;
import lombok.Getter;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.DigestUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @program emcp-framework
 * @ClassName MethodInterceptorHolder
 * @description: aop方法拦截holder类
 * @author: sky
 * @create: 2018/11/17 17:02
 */
@Getter
public class MethodInterceptorHolder {

    private String id;
    private Method method;
    private Object target;
    private Map<String,Object> args;

    public static final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

    /**
     * 构造方法
     * @param id 唯一标识
     * @param method 方法
     * @param target 执行方法的对象
     * @param args 参数映射
     */
    public MethodInterceptorHolder(String id, Method method,
                                   Object target, Map<String, Object> args) {
        Objects.requireNonNull(id);
        Objects.requireNonNull(method);
        Objects.requireNonNull(target);
        Objects.requireNonNull(args);
        this.id = id;
        this.method = method;
        this.target = target;
        this.args = args;
    }

    /**
     * 获取当前holder
     * @return
     */
    public static MethodInterceptorHolder current(){
        return ThreadLocalUtils.get(MethodInterceptorHolder.class.getName());
    }

    /**
     * 删除当前holder
     * @return
     */
    public static MethodInterceptorHolder clear(){
        return ThreadLocalUtils.getAndRemove(MethodInterceptorHolder.class.getName());
    }

    /**
     * 设置holder
     * @param holder
     * @return
     */
    public static MethodInterceptorHolder setCurrent(MethodInterceptorHolder holder){
        return ThreadLocalUtils.put(MethodInterceptorHolder.class.getName(),holder);
    }

    /**
     * 创建holder
     * @param invocation 方法调用器
     * @return
     */
    public static MethodInterceptorHolder create(MethodInvocation invocation){
        String id = DigestUtils.md5DigestAsHex(String.valueOf(invocation.getMethod().hashCode()).getBytes());
        String[] argNames = discoverer.getParameterNames(invocation.getMethod());
        Object[] args = invocation.getArguments();
        Map<String,Object> argMap = AssembleTools.assembleMethodArgMapper(argNames,args);
        return new MethodInterceptorHolder(id,invocation.getMethod(),invocation.getThis(),argMap);
    }

    /**
     * 查找方法注解
     * @param annotation 注解类型
     * @param <T> 泛型
     * @return 注解
     */
    public <T extends Annotation> T findMethodAnnotation(Class<T> annotation){
        return AopUtils.findMethodAnnotation(target.getClass(),method,annotation);
    }

    /**
     * 查找类注解
     * @param annotation 注解类型
     * @param <T> 泛型
     * @return 注解
     */
    public <T extends Annotation> T findClassAnnotation(Class<T> annotation){
        return AnnotationUtils.findAnnotation(target.getClass(),annotation);
    }
    /**
     * 查找注解
     * @param annotation 注解类型
     * @param <T> 泛型
     * @return 注解
     */
    public <T extends Annotation> T findAnnotation(Class<T> annotation){
        return AopUtils.findAnnotation(target.getClass(),method,annotation);
    }

    /**
     * 创建方法参数相关上下文
     * @return 上下文对象
     */
    public MethodInterceptorContext createParamContext(){
        return createParamContext(null);
    }

    /**
     * 创建方法参数相关上下文 指定返回值
     * @param invokeResult 返回值
     * @return 上下文对象
     */
    public MethodInterceptorContext createParamContext(Object invokeResult) {
        return new MethodInterceptorContext() {
            @Override
            public Object getTarget() {
                return target;
            }

            @Override
            public Method getMethod() {
                return method;
            }

            @Override
            public <T> Optional<T> getParameter(String paramName) {
                if (args == null){
                    return Optional.empty();
                }
                return Optional.of((T)args.get(paramName));
            }

            @Override
            public <T extends Annotation> T getAnnotation(Class<T> annotation) {
                return findClassAnnotation(annotation);
            }

            @Override
            public Map<String, Object> getParams() {
                return getArgs();
            }

            @Override
            public Object getInvokeResult() {
                return invokeResult;
            }
        };
    }

}
